<!-- 融合ElForm 和 ElDescriptions，按条件显示ElForm形式还是ElDescriptions形式 -->
<script lang="ts" setup>
import type {
  descriptionProps,
  FormInstance,
  FormItemProps,
  formItemProps,
  FormItemRule,
  FormProps,
} from 'element-plus';
import type { ExtractPropTypes } from 'vue';
import { mergeProps } from 'vue';

import type { BasicOrComponentOpt } from '../basic-or-component/basic-or-component.vue';

export type DescriptionItemProps = ExtractPropTypes<typeof formItemProps>;
export type DescriptionProps = Partial<ExtractPropTypes<typeof descriptionProps>>;

export interface FormDescChild {
  hidden?: () => boolean;
  label?: BasicOrComponentOpt;
  prop?: string;
  rule?: FormItemRule[];
  span?: number;
  formItemProps?: FormItemProps;
  formItemClass?: any;
  formItem?: BasicOrComponentOpt;
  descItemProps?: DescriptionItemProps;
  descItemClass?: any;
  descItem?: BasicOrComponentOpt;
}

export interface FormDescProps {
  type: 'form' | 'desc';
  model?: any;
  column?: number;
  formProps?: Partial<FormProps>;
  formClass?: any;
  descProps?: Partial<DescriptionProps>;
  descClass?: any;
  children: FormDescChild[];
}

const props = withDefaults(defineProps<FormDescProps>(), {
  column: 2,
});
defineOptions({ name: 'FormDesc', inheritAttrs: false });

const rules = computed(() => {
  return props.children?.reduce((rules, next) => {
    next.prop && (rules[next.prop] = next.rule);
    return rules;
  }, {} as any);
});

const formRef = shallowRef<FormInstance>();

defineExpose({
  formRef,
});
</script>

<template>
  <el-form
    v-if="type === 'form'"
    ref="formRef"
    :model="model"
    :rules="rules"
    :label-position="formProps?.labelPosition ?? 'top'"
    class="grid-cols-2 grid!"
    :style="{
      'grid-template-columns': `repeat(${column}, 1fr)`,
    }"
    :class="formClass"
    v-bind="mergeProps($attrs, formProps ?? {})"
  >
    <template v-for="item in children" :key="item.prop">
      <el-form-item
        v-if="!item.hidden?.()"
        :prop="item.prop"
        :style="{
          'grid-column': `span ${item.span ?? 2} / span ${item.span ?? 2}`,
        }"
        :class="item.formItemClass"
      >
        <template #label>
          <BasicOrComponent :is="item.label" />
        </template>
        <component :is="item.formItem" v-bind="item.formItemProps" />
      </el-form-item>
    </template>
  </el-form>
  <el-descriptions
    v-if="type === 'desc'"
    :direction="descProps?.direction ?? 'vertical'"
    :column="column"
    :class="descClass"
    v-bind="mergeProps($attrs, descProps ?? {})"
    class="form-desc--descriptions"
  >
    <template v-for="item in children" :key="item.prop">
      <el-descriptions-item
        v-if="!item.hidden?.()"
        :class="item.descItemProps"
        :span="item.span ?? 2"
        v-bind="item.descItemProps"
      >
        <template #label>
          <BasicOrComponent :is="item.label" />
        </template>
        <component :is="item.descItem" />
      </el-descriptions-item>
    </template>
  </el-descriptions>
</template>

<style lang="scss">
.form-desc--descriptions {
  .el-descriptions__table {
    table-layout: fixed;
  }

  .el-descriptions__cell.el-descriptions__content.is-vertical-content {
    height: 36px;
  }
}
</style>
